package org.chalmers.rl;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

public class Actions {
	//public static final int MAX_BACKTRACK = 10;
	public double initVal;
	
	public Integer myState;
	
	private TreeSet<StateAction> moves = new TreeSet<StateAction>(new Comparator<StateAction>() {

		@Override
		public int compare(StateAction o1, StateAction o2) {
			if (Math.abs(o1.val() - o2.val()) < 1e-20) {
				return o1.hashCode() - o2.hashCode();
			}
			return o1.val() < o2.val() ? -1 : 1;
		}
		
	}); // replace with a queue of fixed size?
	
	private Map<Integer,StateAction> action_sa = new HashMap<Integer,StateAction>();
	
	private HashSet<StateAction> backTrack = new HashSet<StateAction>();
	
	public Actions(Integer state) {
		this.myState = state;
	}
	
	public StateAction observe(Integer action, Integer newState, double reward) {
		StateAction act = byAction(action);
		if (act == null) {
			act = new StateAction(action, myState, initVal);
			moves.add(act);
			action_sa.put(action, act);
		}
		act.observe(newState, reward);
		return act;
	}
	
	// add state-action that lead to this state
	public void addBactrack(StateAction back) {
		backTrack.add(back);
	}
	
	public StateAction[] backTrack() {
		return backTrack.toArray(new StateAction[0]);
	}
	
	public StateAction byAction(Integer action) {
		if (moves.isEmpty()) {
			return null;
		}
//		for (StateAction sa : moves) {
//			if (action.equals(sa.myAct)) {
//				return sa;
//			}
//		}
//		return null;
		return action_sa.get(action);
	}
	
	public TreeSet<StateAction> moves() {
		return moves;
	}
	
	
	
	public double maxVal() {
	   if (moves.isEmpty()) {
		   return initVal;
	   }
	   
//	   double max = moves.iterator().next().val;
//       for (StateAction sa : moves) {
//           if (sa.val > max) {
//               max = sa.val;
//           }
//       }
//       return max;
	   return moves.last().val();
	}
	
	public double minVal() {
	   if (moves.isEmpty()) {
		   return initVal;
	   }
	   
//	   double min = moves.iterator().next().val;
//       for (StateAction sa : moves) {
//           if (sa.val < min) {
//               min = sa.val;
//           }
//       }
//       return min;
	   return moves.first().val();
	}
	
	public Integer greedy() {
//	   double max = maxVal();
       StateAction max = moves.last();
       
       List<StateAction> maxmoves = new ArrayList<StateAction>();
       
       for (StateAction sa : moves.descendingSet()) {
    	   if (Math.abs(sa.val() - max.val()) < 1e-20) {
    		   maxmoves.add(sa);
    	   } else {
    		   break;
    	   }    	   
       }
       
	   //StateAction[] maxmoves = moves.tailSet(max).toArray(new StateAction[0]);
	   
	   
	   
//       List<Integer> maxIndxs = new ArrayList<Integer>();
//       for (StateAction sa : moves) {
//       	if (Math.abs(max - sa.val) < 1e-10) {
//               maxIndxs.add(sa.myAct);
//           }
//       }
//       
//       int len = maxIndxs.size();
//       return maxIndxs.get(Utils.rand.nextInt(len));
	   int len = maxmoves.size();
	   return maxmoves.get(Utils.rand.nextInt(len)).myAct;
	}
	
	public String toString() {
		StringBuffer sb = new StringBuffer();
		sb.append("My state: " + myState)
		  .append(" Moves: ");
		
		for (StateAction m : moves) {
			sb.append("\nAction: " + m.myAct + " \n");
			sb.append("Value: " + m.val() + " \n");
			sb.append("\t Transitions: \n");
			for (Integer state : m.transitions()) {
				sb.append(state + ", ");
			}
			
		}
		sb.append("\nBacktrack: ");
		for (StateAction b : backTrack) {
			sb.append(b.myState + ", ");
		}
		
		return sb.toString();
		
	}
	
//	public void updateTree(StateAction sa) {
//		moves.remove(sa);
//		moves.add(sa);
//	}
	
}
